using System;
using System.Globalization;
using System.IO;
using System.Net;
using System.Threading;
using Microsoft.VisualStudio.WebHost;
using Salient.Net;

namespace Salient.Web.Hosting
{
    /// <summary>
    /// <para>A general purpose Microsoft.VisualStudio.WebHost.Server test fixture.</para>
    /// <para>This server is run in-process and may be used in F5 debugging. </para>
    /// </summary>
    /// <remarks>
    /// <para>If you are adding this source code to a new project, You will need to  manually add a reference to WebDev.WebHost.dll to your project. </para>
    /// <para>It cannot be added from within Visual Studio.</para>
    /// <para>To add a reference you will need to open your .csproj file in a text editor and add the reference manually.</para>
    /// <para>Look for the item group that contains the project references and add the following element:</para>
    /// </remarks>
    /// <example>
    /// &lt;Reference Include="WebDev.WebHost, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=x86">
    ///   &lt;Private>False&lt;/Private>
    /// &lt;/Reference>
    /// </example>
    public class WebHostServer:IWebServer
    {
        private Server _server;

        /// <summary>
        /// 
        /// </summary>
        public string ApplicationPath { get; private set; }

        /// <summary>
        /// 
        /// </summary>
        public string HostName { get; private set; }

        /// <summary>
        /// 
        /// </summary>
        public int Port { get; private set; }

        /// <summary>
        /// 
        /// </summary>
        public string VirtualPath { get; private set; }

        /// <summary>
        /// 
        /// </summary>
        public string RootUrl
        {
            get { return string.Format(CultureInfo.InvariantCulture, "http://{0}:{1}{2}", HostName, Port, VirtualPath); }
        }

        /// <summary>
        /// Combine the RootUrl of the running web application with the relative url
        /// specified.
        /// </summary>
        /// <param name="relativeUrl"></param>
        /// <returns></returns>
        public string NormalizeUrl(string relativeUrl)
        {
            return NetworkUtils.NormalizeUrl(RootUrl, relativeUrl);
        }


        /// <summary>
        /// <para>Starts an instance of WebDev.WebServer.exe on first available port in the range 8000-10000.</para>
        /// </summary>
        /// <remarks>
        /// <para>For use with testing frameworks, you would typically call this method in the Fixture setup method.</para>
        /// </remarks>
        /// <param name="applicationPath"></param>
        public void StartServer(string applicationPath)
        {
            StartServer(applicationPath, NetworkUtils.GetAvailablePort(8000, 10000, IPAddress.Loopback, true), "/", "localhost");
        }

        /// <summary>
        /// <para>Starts an instance of WebDev.WebServer.exe using the supplied parameters.</para>
        /// </summary>
        /// <remarks>
        /// <para>For use with testing frameworks, you would typically call this method in the Fixture setup method.</para>
        /// </remarks>
        /// <param name="applicationPath">Physical path to application.</param>
        /// <param name="port">Port to listen on.</param>
        /// <param name="virtualPath">Optional. defaults to "/"</param>
        /// <param name="hostName">Optional. Is used to construct RootUrl. Defaults to "localhost"</param>
        public void StartServer(string applicationPath, int port, string virtualPath, string hostName)
        {
            if (_server != null)
            {
                throw new InvalidOperationException("Server already started");
            }

            // WebHost.Server will not run on any other IP
            IPAddress ipAddress = IPAddress.Loopback;

            if (!NetworkUtils.IsPortAvailable(ipAddress, port))
            {
                throw new Exception(string.Format("Port {0} is in use.", port));
            }

            applicationPath = Path.GetFullPath(applicationPath);

            virtualPath = String.Format("/{0}/", (virtualPath ?? string.Empty).Trim('/')).Replace("//", "/");

            _server = new Server(port, virtualPath, applicationPath, false, false);
            
            _server.Start();

            ApplicationPath = applicationPath;
            Port = port;
            VirtualPath = virtualPath;
            HostName = string.IsNullOrEmpty(hostName) ? "localhost" : hostName;
        }

        /// <summary>
        /// This overload is invalid for WebHostServer. WebHostServer can only serve on LoopBack.
        /// </summary>
        public void StartServer(string applicationPath, IPAddress ipAddress, int port, string virtualPath, string hostname)
        {
            throw new NotImplementedException("This overload is invalid for WebHostServer. WebHostServer can only serve on LoopBack.");
        }

        /// <summary>
        /// Stops the server.
        /// </summary>
        /// <remarks>
        /// <para>For use with testing frameworks, you would typically call this method in the Fixture teardown method.</para>
        /// <para>If not called explicitely, Dispose will shut down the instance.</para>
        /// </remarks>
        public void StopServer()
        {
            if (_server != null)
            {
                _server.Stop();
                _server = null;
                // allow some time to release the port
                Thread.Sleep(100);
            }
        }

        ///<summary>
        /// Closes the server instance, if any.
        ///</summary>
        public void Dispose()
        {
            StopServer();
        }


    
    }
}